package vm

import (
	"encoding/binary"
	"fmt"
	"log"

	"gitee.com/chuanjinge/go-interpreter-waiig/src/monkey/code"
	"gitee.com/chuanjinge/go-interpreter-waiig/src/monkey/compiler"
	"gitee.com/chuanjinge/go-interpreter-waiig/src/monkey/object"
)

const StackSize = 2048 // 栈的大小
const GloablSize = 65535

var (
	TRUE  = &object.Boolean{Value: true}
	FALSE = &object.Boolean{Value: false}
	Null  = &object.Null{}
)

// 当前执行字节码的虚拟机使用的是 栈模式的虚拟机，通过压栈弹栈的方式实现指令的运算和执行
type VM struct {
	// byteCode *compiler.Bytecode
	constants    []object.Object
	instructions code.Instructions
	symbolTable  *compiler.SymbolTable
	stack        []object.Object
	sp           int // 始终指向栈中的下一个空闲槽。相当于寄存器中的SP寄存器（SP一般和SS配合，前者是指针寄存器，后者是段寄存器, SS:SP 找到栈顶位置）
	// 全局变量池，绑定全局变量索引和全局变量值，索引是在编译过程中生成的存储在symbolTable中，值是通过栈不断的POP出来的
	// 该池子相当于symboltable的具象数据
	globals []object.Object
}

func New(bc *compiler.Bytecode) *VM {
	return &VM{
		constants:    bc.Constants,
		instructions: bc.Instructions,
		symbolTable:  bc.SymbolTable,
		stack:        make([]object.Object, StackSize),
		sp:           0,
		globals:      make([]object.Object, GloablSize),
	}
}

func NewWithState(bc *compiler.Bytecode, globals []object.Object) *VM {
	vm := New(bc)
	vm.globals = globals
	return vm
}

// 执行目标的字节码
// 目前只实现压栈操作
func (vm *VM) Run() error {
	// 遍历指令集
	// 针对OpConstant操作码，执行压栈操作
	// ip 游标，遍历指令字节码使用 - 模拟寄存器中的IP寄存器，配合CS-代码段寄存器进行代码的寻址，即用CS:IP来确定下一条代码的执行位置
	for ip := 0; ip < len(vm.instructions); ip++ {
		op := vm.instructions[ip] // 当前操作码的字节形式
		opCode := code.Opcode(op) // 转为制式
		switch opCode {
		case code.OpConstant:
			// 常量操作数操作码直接操作2字节的内存空间，此处直接硬编码了，根本没有管 definition 中的的明文说明。
			constIdx := binary.BigEndian.Uint16(vm.instructions[ip+1:])
			ip += 2 // 移动到操作数的最后一个字节处，等待 ip++
			// 基于constIdx从常量池中找到对应的数据对象并压栈
			log.Printf("~~~~~constant:%d\n", constIdx)
			obj := vm.constants[constIdx]
			err := vm.StackPush(obj)
			if err != nil {
				return err
			}
		case code.OpAdd:
			err := vm.runAdd()
			if err != nil {
				return err
			}
		case code.OpSub:
			err := vm.runSub()
			if err != nil {
				return err
			}
		case code.OpMul:
			err := vm.runMul()
			if err != nil {
				return err
			}
		case code.OpDiv:
			err := vm.runDiv()
			if err != nil {
				return err
			}
		case code.OpPop:
			log.Println("~~~~~pop")
			// 如果碰到了pop指令，就意味着要清理最近一个栈顶元素了
			vm.StackPop()
		case code.OpTrue:
			err := vm.StackPush(TRUE)
			if err != nil {
				return err
			}
		case code.OpFalse:
			err := vm.StackPush(FALSE)
			if err != nil {
				return err
			}
		case code.OpEqual, code.OpNotEqual, code.OpGreaterThan:
			err := vm.runCompare(opCode)
			if err != nil {
				return err
			}
		// 前缀运算符操作码
		case code.OpMinus:
			err := vm.runMinus()
			if err != nil {
				return err
			}
		case code.OpBang:
			err := vm.runBang()
			if err != nil {
				return err
			}
		case code.OpJump:
			// 弹栈判断 true/false，决定继续执行还是跳跃到指定偏移量再执行
			pos := int(code.ReadUint16(vm.instructions, ip+1))
			ip = pos - 1 // 之所以-1，是为了兼容当前循环的下一次 ip++
		case code.OpJumpNotTruthy:
			var err error
			ip, err = vm.runJumpNotTruthy(ip)
			if err != nil {
				return err
			}
		case code.OpNull:
			err := vm.StackPush(Null)
			if err != nil {
				return err
			}
		case code.OpSetGlobal:
			log.Println("~~~~~setglobal")
			// 取出栈顶元素，set到全局变量池中
			pos := int(code.ReadUint16(vm.instructions, ip+1))
			value := vm.StackPop()
			vm.globals[pos] = value
			ip += 2
		case code.OpGetGlobal:
			log.Println("~~~~~getglobal")
			pos := int(code.ReadUint16(vm.instructions, ip+1))
			ip += 2
			if pos >= len(vm.globals) {
				return fmt.Errorf("symbol not found")
			}
			// 从全局变量池中取出目标符号的值，将该对象压栈以便后续执行中缀运算
			err := vm.StackPush(vm.globals[pos])
			if err != nil {
				return err
			}
		case code.OpArray:
			// 数组操作
			count := int(code.ReadUint16(vm.instructions, ip+1))
			ip += 2
			// 从栈中取出目标数据构造为object.Array
			// 当前槽指针指向 vm.sp 表示栈顶的上一个元素，栈顶及其之下的共 count 个元素构成数组元素，所以我们需要从 vm.sp-count 开始
			elements := make([]object.Object, count)
			j := 0
			for i := vm.sp - count; i < count; i++ {
				elements[j] = vm.stack[i]
				j++
			}

			arr := &object.Array{Elements: elements}
			vm.sp = vm.sp - count
			err := vm.StackPush(arr) // TODO
			if err != nil {
				return err
			}
		case code.OpHash:
			count := int(code.ReadUint16(vm.instructions, ip+1))
			ip += 2

			pairs := make(map[object.HashKey]object.HashPair)
			for i := vm.sp - count; i < count; i = i + 2 {
				hashKeyObj, ok := vm.stack[i].(object.Hashable)
				if !ok {
					return fmt.Errorf("unusable as hash key: %s", vm.stack[i].Type())
				}
				hashKey := hashKeyObj.HashKey()
				pairs[hashKey] = object.HashPair{
					Key:   vm.stack[i],
					Value: vm.stack[i+1],
				}
			}

			h := &object.Hash{Pairs: pairs}
			err := vm.StackPush(h)
			if err != nil {
				return err
			}
		case code.OpIndex:
			// 索引求值 - 从栈中弹出索引对象，然后弹出数组或者哈希对象，然后从后者中取出目标
			indexObj := vm.StackPop()
			compositeObj := vm.StackPop()

			if compositeObj.Type() == object.ARRAY_OBJ && indexObj.Type() == object.INTEGER_OBJ {
				err := vm.runArrayIndex(compositeObj, indexObj)
				if err != nil {
					return err
				}
			} else if compositeObj.Type() == object.HASH_OBJ {
				err := vm.runHashIndex(compositeObj, indexObj)
				if err != nil {
					return err
				}
			} else {
				return fmt.Errorf("index operator not supported: %s", compositeObj.Type())
			}
		}
	}
	return nil
}

// !+[op] 执行各种基于栈顶元素的操作
func (vm *VM) runAdd() error {
	// 相加操作，将左右对象弹栈，并执行相加
	right := vm.StackPop()
	left := vm.StackPop()

	if right == nil || left == nil {
		return fmt.Errorf("operate add error, left or right operands nil")
	}

	if right.Type() == object.INTEGER_OBJ && left.Type() == object.INTEGER_OBJ {
		rightInt := right.(*object.Integer)
		leftInt := left.(*object.Integer)
		addResult := &object.Integer{Value: (rightInt.Value + leftInt.Value)}
		// 将该结果压栈
		vm.StackPush(addResult)
		return nil
	} else if right.Type() == object.STRING_OBJ && left.Type() == object.STRING_OBJ {
		rightStr := right.(*object.String)
		leftStr := left.(*object.String)
		addResult := &object.String{Value: (leftStr.Value + rightStr.Value)}
		// 将该结果压栈
		vm.StackPush(addResult)
		return nil
	} else {
		return fmt.Errorf("operate add not support current type:left:%s,right:%s", right.Type(), left.Type())
	}
}

func (vm *VM) runSub() error {
	right := vm.StackPop()
	left := vm.StackPop()

	if right == nil || left == nil {
		return fmt.Errorf("operate sub error, left or right operands nil")
	}

	if right.Type() == object.INTEGER_OBJ && left.Type() == object.INTEGER_OBJ {
		rightInt := right.(*object.Integer)
		leftInt := left.(*object.Integer)
		addResult := &object.Integer{Value: (leftInt.Value - rightInt.Value)}
		// 将该结果压栈
		vm.StackPush(addResult)
		return nil
	} else {
		return fmt.Errorf("operate sub not support current type:left:%s,right:%s", right.Type(), left.Type())
	}
}

func (vm *VM) runMul() error {
	right := vm.StackPop()
	left := vm.StackPop()

	if right == nil || left == nil {
		return fmt.Errorf("operate sub error, left or right operands nil")
	}

	if right.Type() == object.INTEGER_OBJ && left.Type() == object.INTEGER_OBJ {
		rightInt := right.(*object.Integer)
		leftInt := left.(*object.Integer)
		addResult := &object.Integer{Value: (leftInt.Value * rightInt.Value)}
		// 将该结果压栈
		vm.StackPush(addResult)
		return nil
	} else {
		return fmt.Errorf("operate sub not support current type:left:%s,right:%s", right.Type(), left.Type())
	}
}

func (vm *VM) runDiv() error {
	right := vm.StackPop()
	left := vm.StackPop()

	if right == nil || left == nil {
		return fmt.Errorf("operate sub error, left or right operands nil")
	}

	if right.Type() == object.INTEGER_OBJ && left.Type() == object.INTEGER_OBJ {
		rightInt := right.(*object.Integer)
		leftInt := left.(*object.Integer)
		addResult := &object.Integer{Value: (leftInt.Value / rightInt.Value)}
		// 将该结果压栈
		vm.StackPush(addResult)
		return nil
	} else {
		return fmt.Errorf("operate sub not support current type:left:%s,right:%s", right.Type(), left.Type())
	}
}

func (vm *VM) runCompare(op code.Opcode) error {
	right := vm.StackPop()
	left := vm.StackPop()

	if right == nil || left == nil {
		return fmt.Errorf("operate compare error, left or right operands nil")
	}

	// 按照对象类型的不同来分类比较
	if right.Type() == object.INTEGER_OBJ && left.Type() == object.INTEGER_OBJ {
		return vm.runCompareInteger(op, left, right)
	}

	// 其他类型的比较，按照比较符的不同视情况而定
	switch op {
	case code.OpEqual:
		// 相等的比较，完全按照内存地址是否一致来比较（浅比较）
		return vm.StackPush(nativeBoolToBooleanObject(right == left))
	case code.OpNotEqual:
		return vm.StackPush(nativeBoolToBooleanObject(right != left))
	default:
		// 其他情况暂不支持
		return fmt.Errorf("unknown compare operator: %d:left:%s,right:%s", op, right.Type(), left.Type())
	}

}

func (vm *VM) runCompareInteger(op code.Opcode, left object.Object, right object.Object) error {
	rightInt := right.(*object.Integer)
	leftInt := left.(*object.Integer)

	switch op {
	case code.OpEqual:
		return vm.StackPush(nativeBoolToBooleanObject(leftInt.Value == rightInt.Value))
	case code.OpNotEqual:
		return vm.StackPush(nativeBoolToBooleanObject(leftInt.Value != rightInt.Value))
	case code.OpGreaterThan:
		return vm.StackPush(nativeBoolToBooleanObject(leftInt.Value > rightInt.Value))
	}

	return nil
}

func (vm *VM) runMinus() error {
	right := vm.StackPop()

	if right == nil {
		return fmt.Errorf("operate minus error, right operands nil")
	}

	if right.Type() == object.INTEGER_OBJ {
		rightInt := right.(*object.Integer)
		addResult := &object.Integer{Value: (0 - rightInt.Value)}
		// 将该结果压栈
		vm.StackPush(addResult)
		return nil
	} else {
		return fmt.Errorf("operate minus not support current type:,right:%s", right.Type())
	}
}

func (vm *VM) runBang() error {
	right := vm.StackPop()

	if right == nil {
		return fmt.Errorf("operate bang error, right operands nil")
	}

	if right.Type() == object.INTEGER_OBJ {
		rightInt := right.(*object.Integer)
		if rightInt.Value == 0 {
			vm.StackPush(TRUE)
		} else {
			vm.StackPush(FALSE)
		}
		return nil
	} else if right.Type() == object.BOOLEAN_OBJ {
		rightBol := right.(*object.Boolean)
		if rightBol == FALSE {
			vm.StackPush(TRUE)
		} else {
			vm.StackPush(FALSE)
		}
		return nil
	} else if right == Null {
		vm.StackPush(TRUE)
		return nil
	} else {
		return fmt.Errorf("operate bang not support current type:,right:%s", right.Type())
	}
}

// 执行跳转偏移量的操作
// 第一个返回值是偏移量
func (vm *VM) runJumpNotTruthy(ip int) (int, error) {
	right := vm.StackPop()

	if right == nil {
		return ip, fmt.Errorf("operate JumpNotTruthy error, right operands nil")
	}

	condition := true
	if right.Type() == object.BOOLEAN_OBJ {
		rightBol := right.(*object.Boolean)
		condition = rightBol.Value
	} else if right.Type() == object.INTEGER_OBJ {
		rightInt := right.(*object.Integer)
		condition = (rightInt.Value != 0)
	} else if right == Null {
		condition = false
	}

	log.Printf("~~~~~condition:%t\n", condition)
	// 条件表达式为假的话，跳到存储的偏移量，微针的话，就直接移动2个偏移量执行结果即可
	if condition {
		// 移动 ip 指针，进入下次循环即可
		// 因为OpJump的操作数是2字节的，所以移动2个字节即可
		return ip + 2, nil
	} else {
		pos := int(code.ReadUint16(vm.instructions, ip+1))
		return pos - 1, nil
	}
}

func (vm *VM) runArrayIndex(arr object.Object, idx object.Object) error {
	arrObj := arr.(*object.Array)
	idxObj := idx.(*object.Integer)

	index := idxObj.Value
	maxIdx := int64(len(arrObj.Elements) - 1)
	if index > maxIdx || index < 0 {
		return vm.StackPush(Null)
	}

	log.Printf("~~~~~ maxIdx: %d\n", maxIdx)
	log.Printf("~~~~~ index: %d\n", index)

	return vm.StackPush(arrObj.Elements[index])

}

func (vm *VM) runHashIndex(hash, index object.Object) error {
	hashObject := hash.(*object.Hash)
	key, ok := index.(object.Hashable)
	if !ok {
		return fmt.Errorf("unusable as hash key: %s", index.Type())
	}

	pair, ok := hashObject.Pairs[key.HashKey()]
	if !ok {
		return vm.StackPush(Null)
	}

	return vm.StackPush(pair.Value)
}

// !-[op]

func nativeBoolToBooleanObject(value bool) *object.Boolean {
	if value {
		return TRUE
	} else {
		return FALSE
	}
}

// !+[op]

func (vm *VM) StackTop() object.Object {
	if vm.sp <= 0 {
		return nil
	}

	return vm.stack[vm.sp-1]
}

// 压栈
func (vm *VM) StackPush(obj object.Object) error {
	if vm.sp >= StackSize {
		return fmt.Errorf("stack overflow")
	}
	vm.stack[vm.sp] = obj
	vm.sp += 1
	return nil
}

// 弹栈
func (vm *VM) StackPop() object.Object {
	if vm.sp <= 0 {
		return nil
	}

	obj := vm.stack[vm.sp-1]
	vm.sp -= 1
	return obj
}

// 获取最后一个被pop出去的对象
// 因为 sp指向栈顶之顶的元素位置，也就是为了在此处压栈形成新的栈顶，所以理论上刚刚被弹栈出去后sp指向的就是弹栈的地方，虽然在逻辑栈中不再可用，但是可以在调试的时候使用该元素
func (vm *VM) LastPoppedStackElem() object.Object {
	obj := vm.stack[vm.sp]
	return obj
}
